﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using LeeAnn;
using LeeAnn.Framework;
using LeeAnn.FeedForward;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Graphics;
using System.Collections;

namespace Demo1
{
    public class DeanCreature
    {
        public FeedForwardAnn Brain;
        public Vector2D Vec;
        private int window_width, window_height = 0;
        private Vector2D tempVec;
        public Matrix mDean;
        public double Speed = 1;
        public double Rotation = 0;
        private bool goto1, goto2 = false;
        public int Width;
        public int Height;
        private long lastReward;
        public int closestApple = 0;
        public int DistanceToClosestApple = 0;
        public int Rewards { get; set; }
        public int Punishments {get;set;}
        public DeanCreature(int WindowWidth, int WindowHeight)
        {
            Vec = new Vector2D((float)Maths.RandomClamped(0, WindowWidth), (float)Maths.RandomClamped(0, WindowHeight));
            window_width = WindowWidth;
            window_height = WindowHeight;
            tempVec = new Vector2D(0, 0);
            lastReward = DateTime.Now.Ticks;
            InitBrain();
        }
        private void InitBrain()
        {
            InitBrain(4, 1);
        }
        public void InitBrain(int width, int height)
        {
            Rewards = 0;
            Punishments = 0;
            Width = width;
            Height = height;
            Brain = new FeedForwardAnn(width, height);
            ArrayList inouts = new ArrayList();
            for (int i = 0; i < Brain.Width; i++)
            {
                inouts.Add(new FeedForwardNeuronPlaceholder(new Position3D(i, 0, 0)));
            }
            Brain.addInputNeuron(inouts, new Neuron.CalculateIODelegate(Brain.CalculateEvent));
            Brain.addInputNeuron(inouts, new Neuron.CalculateIODelegate(Brain.CalculateEvent));
            //Brain.addInputNeuron(inouts, new Neuron.CalculateIODelegate(Brain.CalculateEvent));

            ArrayList outins = new ArrayList();
            for (int i = 0; i < Brain.Width; i++)
            {
                outins.Add(new FeedForwardNeuronPlaceholder(new Position3D(i, height-1, 0)));
            }

            Brain.addOutputNeuron(outins, new Neuron.CalculateIODelegate(Brain.CalculateEvent));
            Brain.InputOutputNeurons[Brain.Outputs[0]].OutputEvent += new Neuron.OutputDelegate(onOutput1);
            Brain.addOutputNeuron(outins, new Neuron.CalculateIODelegate(Brain.CalculateEvent));
            Brain.InputOutputNeurons[Brain.Outputs[1]].OutputEvent += new Neuron.OutputDelegate(onOutput2);
        }
        private void Move(ref List<Vector2D> appleLocation, Texture2D DeanTex, Texture2D AppleTex, Color[,] DeanColor, Color[,] AppleColor)
        {
            if (goto1 && goto2)
            {
                Vec.Angle = (float)Rotation;
                if (Vec.X > window_width) Vec.X = 0;
                if (Vec.X < 0) Vec.X = window_width;
                if (Vec.Y > window_height) Vec.Y = 0;
                if (Vec.Y < 0) Vec.Y = window_height;
                
                Vec.MoveUp();

                if (Vec.X >= appleLocation[closestApple].X - DeanTex.Width)
                {
                    if (Vec.X <= appleLocation[closestApple].X + (AppleTex.Width))
                    {
                        if (Vec.Y >= appleLocation[closestApple].Y - DeanTex.Height)
                        {
                            if (Vec.Y <= appleLocation[closestApple].Y + (AppleTex.Height))
                            {
                                Rewards++;
                                Brain.Reward();
                                lastReward = DateTime.Now.Ticks;
                                appleLocation[closestApple] = new Vector2D((float)Maths.RandomClamped(AppleTex.Width, this.window_width - AppleTex.Width), (float)Maths.RandomClamped(AppleTex.Height, this.window_height - AppleTex.Height));
                            }
                        }
                    }
                }
                if (new DateTime(lastReward).AddSeconds(10).Ticks <= DateTime.Now.Ticks)
                {
                    Punishments++;
                    lastReward = DateTime.Now.Ticks;
                    Brain.Punish();
                }
                if (Punishments == 50)
                {
                    InitBrain(Width, Height);
                }
                goto1 = false;
                goto2 = false;
            }
        }
        public void Update(ref List<Vector2D> appleLocation,Texture2D DeanTex,Texture2D AppleTex, Color[,] DeanColor, Color[,] AppleColor)
        {
            FindClosestApple(appleLocation);
            double tehangle = Maths.RoundClamp(Maths.PointsToAngle(Vec, appleLocation[closestApple]) - Vec.Angle, -360, 360);
            Brain.GetInputNeuron(0).Input(tehangle);
            Brain.GetInputNeuron(1).Input(Maths.DistanceBetweenPoints(Vec,appleLocation[closestApple]));
            Brain.Update();
            Move(ref appleLocation,DeanTex,AppleTex,DeanColor,AppleColor);
        }
        private void FindClosestApple(List<Vector2D> Apples)
        {
            double lowestValue = Double.MaxValue;
            double temp = 0;
            int curLow = 0;
            for (int i = 0; i < Apples.Count; i++)
            {
                temp = Maths.DistanceBetweenPoints(Vec,Apples[i]);
                if (temp < lowestValue)
                {
                    lowestValue = temp;
                    curLow = i;
                }
            }
            closestApple = curLow;
        }
        private void onOutput1(double data)
        {
            //tempVec.Rotation = data;
            data = data - (1 - data);
            Rotation += (float) data*5 ;
            goto1 = true;
        }
        private void onOutput2(double data)
        {
            Vec.Speed = (float)data*5;
            goto2 = true;
        }
    }
}
